package com.ljc;

import com.ljc.dto.TreeNode;
import org.apache.commons.lang3.time.StopWatch;


/**
 * @author clj
 * @date 2022/12/5
 * @desc
 * Given a binary tree, determine if it is height-balanced.
 *
 * Example 1:
 * Input: root = [3,9,20,null,null,15,7]
 * Output: true
 *
 *  Example 2:
 * Input: root = [1,2,2,3,3,null,null,4,4]
 * Output: false
 *
 *  Example 3:
 * Input: root = []
 * Output: true
 */
public class E110BalancedBinaryTree {
    /**
     * 这道题中的平衡二叉树的定义是：二叉树的每个节点的左右子树的高度差的绝对值不超过 1，则二叉树是平衡二叉树。
     * 根据定义，一棵二叉树是平衡二叉树，当且仅当其所有子树也都是平衡二叉树，
     * 因此可以使用递归的方式判断二叉树是不是平衡二叉树，递归的顺序可以是自顶向下或者自底向上。
     * @param args
     */
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);

//        root.left.left = new TreeNode(4);
//        root.left.right = new TreeNode(5);

        root.right.left = new TreeNode(6);
//        root.right.right = new TreeNode(7);

        root.right.left.right = new TreeNode(13);

        root.right.left.right.left = new TreeNode(15);
        root.right.left.right.left.right = new TreeNode(16);
        root.right.left.right.left.right.left = new TreeNode(17);

        StopWatch watch1 = new StopWatch();
        watch1.start();
        boolean r1 = isBalanced1(root);
        watch1.stop();
        System.out.println(r1 + " times: " + watch1.getTime());

        StopWatch watch2 = new StopWatch();
        watch2.start();
        boolean r2 = isBalanced2(root);
        watch2.stop();
        System.out.println(r2 + " times: " + watch2.getTime());
    }

    /**
     * 方法一：自顶向下
     * @param root
     * @return
     * 定义函数 height，用于计算二叉树中的任意一个节点 p 的高度：
     *
     * height(p)={ 0 ,  p 是空节点
     *             max(height(p.left),height(p.right))+1 ，p 是非空节点
     *
     * 有了计算节点高度的函数，即可判断二叉树是否平衡。
     * 具体做法类似于二叉树的前序遍历，即对于当前遍历到的节点, 首先计算左右子树的高度，如果左右子树的高度差是否不超过 1，再分别递归地遍历左右子节点，并判断左子树和右子树是否平衡。
     * 这是一个自顶向下的递归的过程。
     */
    public static boolean isBalanced1(TreeNode root) {
        if (root == null) {
            return true;
        } else {
            System.out.println("root1 : " + root);
            return Math.abs(height1(root.left) - height1(root.right)) <= 1 && isBalanced1(root.left) && isBalanced1(root.right);
        }
    }

    public static int height1(TreeNode root) {
        if (root == null) {
            return 0;
        } else {
            return Math.max(height1(root.left), height1(root.right)) + 1;
        }
    }


    /**
     * 方法二：自底向上的递归
     * 方法一由于是自顶向下递归，因此对于同一个节点，函数 height 会被重复调用，导致时间复杂度较高。如果使用自底向上的做法，则对于每个节点，函数 height 只会被调用一次。
     *
     * 自底向上递归的做法类似于后序遍历，对于当前遍历到的节点，先递归地判断其左右子树是否平衡，再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的，则返回其高度（高度一定是非负整数），否则返回 −1。
     * 如果存在一棵子树不平衡，则整个二叉树一定不平衡。
     */
    public static boolean isBalanced2(TreeNode root) {
        return height2(root) >= 0;
    }

    public static int height2(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = height2(root.left);
        int rightHeight = height2(root.right);
        if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
            return -1;
        } else {
            System.out.println("root2 : " + root);
            return Math.max(leftHeight, rightHeight) + 1;
        }
    }


}
